﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SoftwareMonkeys.LogClimber.Entities;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;

namespace SoftwareMonkeys.LogClimber.Business
{
    public class LogGrower
    {
        private string logsDirectoryPath;
        /// <summary>
        /// Gets/sets the full path to the directory containing the logs.
        /// </summary>
        public string LogsDirectoryPath
        {
            get { return logsDirectoryPath; }
            set { logsDirectoryPath = value; }
        }

        private string stackStraceDirectoryName = "StackTrace";
        /// <summary>
        /// Gets/sets the name of the directory containing the stack traces of all the log entries.
        /// </summary>
        public string StackStraceDirectoryName
        {
            get { return stackStraceDirectoryName; }
            set { stackStraceDirectoryName = value; }
        }

        /// <summary>
        /// Sets the logs directory path.
        /// </summary>
        /// <param name="logsDirectoryPath"></param>
        public LogGrower(string logsDirectoryPath)
        {
            LogsDirectoryPath = logsDirectoryPath;
        }

        /// <summary>
        /// Grows the provided linear log into a log tree.
        /// </summary>
        /// <param name="linearLog">The linear log document.</param>
        /// <returns>A tree structured version of the linear log.</returns>
        public Log GrowTree(XmlDocument linearLog)
        {
            Log tree = new Log();

            foreach (Entry entry in this.GetEntriesFromLog(linearLog))
            {
                AddEntryToTree(entry, tree);
            }

            return tree;
        }

        /// <summary>
        /// Grows the specified linear log into a log tree.
        /// </summary>
        /// <param name="log">The linear log.</param>
        /// <returns>A tree structured version of the linear log.</returns>
        public Log GrowTreeFromFile(string logFilePath)
        {
            Log tree = new Log();

            string repairedPath = new LogRepairer().RepairLogFile(this, logFilePath);

            foreach (Entry entry in GetEntriesFromLogFile(repairedPath))
            {
                AddEntryToTree(entry, tree);
            }

            return tree;
        }

        /// <summary>
        /// Retrieves all the entries in the specified log file.
        /// </summary>
        /// <param name="logFilePath">The path to the log file.</param>
        /// <returns>The entries from the log file.</returns>
        public Entry[] GetEntriesFromLogFile(string logFilePath)
        {
            if (!File.Exists(logFilePath))
                throw new ArgumentException("Can't find log file: " + logFilePath);

            XmlDocument document = new XmlDocument();
            document.Load(logFilePath);

            return GetEntriesFromLog(document);
        }

        /// <summary>
        /// Retrieves all the entries from the provided log document.
        /// </summary>
        /// <param name="document">The XML document containing the linear log data.</param>
        /// <returns>The entries loaded from the provided log document.</returns>
        public Entry[] GetEntriesFromLog(XmlDocument document)
        {
            List<Entry> entries = new List<Entry>();

            foreach (XmlNode node in document.DocumentElement.ChildNodes)
            {
                if (node is XmlElement)
                    entries.Add(GetEntryFromElement((XmlElement)node));
            }

            return entries.ToArray();
        }

        /// <summary>
        /// Adds the provided entry to the appropriate location on the tree.
        /// </summary>
        /// <param name="entry">The entry to add to the tree.</param>
        /// <param name="tree">The tree to add the entry to.</param>
        public void AddEntryToTree(Entry entry, Log tree)
        {
            Entry parent = GetParent(tree, entry);

            if (parent == null)
                tree.AppendChild(entry);
            else
                parent.AppendChild(entry);
        }

        /// <summary>
        /// Converts the provided XML element into a log entry.
        /// </summary>
        /// <param name="element">The XML element to convert into a log entry.</param>
        /// <returns>The entry created based on the provided element.</returns>
        public Entry GetEntryFromElement(XmlElement element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            Entry entry = new Entry();

            entry.Data = element.SelectSingleNode("Data").InnerText;
            entry.Component = element.SelectSingleNode("Component").InnerText;
            entry.Method = element.SelectSingleNode("Method").InnerText;
            entry.Indent = Convert.ToInt32(element.SelectSingleNode("Indent").InnerText);
            entry.ParentID = new Guid(element.SelectSingleNode("ParentID").InnerText);
            entry.ID = new Guid(element.SelectSingleNode("ID").InnerText);

            return entry;
        }

        /// <summary>
        /// Retrieves the parent of the provided entry.
        /// </summary>
        /// <param name="log">The log containing the parent.</param>
        /// <param name="entry">The entry to retrieve the parent of.</param>
        /// <returns>The parent entry of the one provided.</returns>
        public Entry GetParent(Log log, Entry entry)
        {
            Guid parentID = entry.ParentID;

            Entry parentEntry = GetEntryWithID(log, parentID);

            return parentEntry;
        }


        /// <summary>
        /// Retrieves the entry from the provided log that has the specified ID.
        /// </summary>
        /// <param name="log">The log to look through for the entry with the specified ID.</param>
        /// <param name="entryID">The ID of the entry to look for.</param>
        /// <returns>The entry with the specified ID, if found, otherwise null.</returns>
        public Entry GetEntryWithID(Log log, Guid entryID)
        {
            return GetEntryWithID(log.ChildEntries, entryID);
        }

        /// <summary>
        /// Retrieves the entry from the provided array that has the specified ID.
        /// </summary>
        /// <param name="entries">The entries to look through for the entry with the specified ID.</param>
        /// <param name="entryID">The ID of the entry to look for.</param>
        /// <returns>The entry with the specified ID, if found, otherwise null.</returns>
        public Entry GetEntryWithID(Entry[] entries, Guid entryID)
        {
            Entry foundEntry = null;
            foreach (Entry entry in entries)
            {
                if (foundEntry == null && entry.ID == entryID)
                {
                    foundEntry = entry;
                }

                if (foundEntry == null)
                    foundEntry = GetEntryWithID(entry.ChildEntries, entryID);
            }
            return foundEntry;
        }
        
    }
}
